package org.jsola.hr.service.excel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import lombok.extern.slf4j.Slf4j;
import org.jsola.common.ValidationResult;
import org.jsola.common.ValidatorKit;
import org.jsola.exception.ParamException;
import org.jsola.hr.vo.excel.ParseResultVO;
import org.jsola.user.core.TokenUser;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author zhr
 */
@Slf4j
public abstract class BaseAnalysisEventListener<T> extends AnalysisEventListener<T> {

    /**
     * 最多解析多少条数据
     */
    private static final int MAX_PARSE_COUNT = 10000;

    /**
     * 每隔2000条存储数据库，存储数量不得超过2000
     */
    private static final int BATCH_COUNT = 2000;

    /**
     * 当前登录用户
     */
    protected TokenUser tokenUser;

    /**
     * 是否只解析
     */
    protected boolean parseOnly = false;

    /**
     * true忽略错误的行，false有错误中断上传
     */
    protected Boolean force = false;

    /**
     * 解析结果
     */
    protected List<ParseResultVO<T>> parseResultList = new ArrayList<>();

    /**
     * 导入成功的数量
     */
    protected int successCount = 0;

    /**
     * 缓存要入库的数据
     */
    protected List<T> dataList = new ArrayList<>();

    @Override
    public void invoke(T data, AnalysisContext context) {
        // 当前行号
        int row = context.readRowHolder().getRowIndex();
        ParseResultVO<T> parseResultVO = parse(data, context);
        // 如果是只解析，则返回直接
        if (parseOnly) {
            ParamException.isFalse(parseResultList.size() > MAX_PARSE_COUNT, "一次解析数量不能超过" + MAX_PARSE_COUNT);
            parseResultList.add(parseResultVO);
            return;
        }
        // 如果是导入，校验不通过，则直接执行异常处理
        if (!parseResultVO.getSuccess()) {
            String firstErrorMsg = parseResultVO.getMsgList().get(0);
            onException(new ParamException(firstErrorMsg), context);
            return;
        }

        dataList.add(data);
        ParamException.isFalse(dataList.size() > BATCH_COUNT, "一次导入数量不能超过" + BATCH_COUNT);

    }

    /**
     * 解析数据。如果想自己解析判断（比如校验数据库是否已存在），可以覆盖该方法
     *
     * @param data    数据
     * @param context excel行数据
     * @return 解析结果
     */
    protected ParseResultVO<T> parse(T data, AnalysisContext context) {
        // 校验数据
        ValidationResult validationResult = ValidatorKit.validateEntity(data);
        ParseResultVO<T> parseResultVO = new ParseResultVO<>();
        // 当前行的数据
        parseResultVO.setData(data);
        // 当前行号
        int row = context.readRowHolder().getRowIndex();
        parseResultVO.setRow(row);
        // 解析是否成功
        parseResultVO.setSuccess(!validationResult.hasErrors());
        // 设置出错信息
        if (validationResult.hasErrors()) {
            List<String> msgList = new ArrayList<>(validationResult.getErrorMsg().values());
            parseResultVO.setMsgList(msgList);
        }
        return parseResultVO;
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据，确保最后遗留的数据也存储到数据库
        if (!CollectionUtils.isEmpty(dataList)) {
            saveData();
        }
    }

    @Override
    public void onException(Exception exception, AnalysisContext context) {
        ParseResultVO<T> parseResultVO = new ParseResultVO<>();
        // 当前行号
        int row = context.readRowHolder().getRowIndex();
        parseResultVO.setRow(row);
        // 解析是否成功
        parseResultVO.setSuccess(Boolean.FALSE);
        // 设置出错信息
        parseResultVO.setMsgList(Collections.singletonList(exception.getMessage()));
        if (parseOnly) {
            parseResultList.add(parseResultVO);
        } else {
            if (!force) {
                log.error("解析excel出错，第" + row + "行", exception);
                if (exception instanceof com.alibaba.excel.exception.ExcelDataConvertException) {
                    throw new ParamException("解析excel出错，第" + row + "行，日期格式错误，正确示例：2000-01-01");
                }
                throw new ParamException("第" + row + "行:" + exception.getMessage());
            } else {
                log.warn("忽略错误，解析excel出错，第" + row + "行", exception);
            }
        }
    }

    /**
     * 入库
     */
    public abstract void saveData();

    public List<ParseResultVO<T>> getParseResultList() {
        return parseResultList;
    }

    public int getSuccessCount() {
        return successCount;
    }

    public TokenUser getTokenUser() {
        return tokenUser;
    }

    public void setTokenUser(TokenUser tokenUser) {
        this.tokenUser = tokenUser;
    }

    public boolean isParseOnly() {
        return parseOnly;
    }

    public void setParseOnly(boolean parseOnly) {
        this.parseOnly = parseOnly;
    }

    public Boolean getForce() {
        return force;
    }

    public void setForce(Boolean force) {
        this.force = force == null ? false : force;
    }
}
